//
//  ALAssetsLibrary category to handle a custom photo album
//
//  Created by Marin Todorov on 10/26/11.
//  Copyright (c) 2011 Marin Todorov. All rights reserved.
//

#import "ALAssetsLibrary+CustomPhotoAlbum.h"
#import <Photos/Photos.h>

#if !__has_feature(objc_arc)
#error This class requires automatic reference counting (ARC).
#endif

@interface ALAssetsLibrary (Private)

/*! A block wraper to be executed after asset adding process done. (Private)
 *
 * \param albumName Custom album name
 * \param completion Block to be executed when succeed to add the asset to the assets library (camera roll)
 * \param failure Block to be executed when failed to add the asset to the custom photo album
 */
- (ALAssetsLibraryWriteImageCompletionBlock)_resultBlockOfAddingToAlbum:(NSString *)albumName
                                                             completion:(ALAssetsLibraryWriteImageCompletionBlock)completion
                                                                failure:(ALAssetsLibraryAccessFailureBlock)failure;

/*! A block wraper to be executed after |-assetForURL:resultBlock:failureBlock:| succeed.
 *  Generally, this block will be excused when user confirmed the application's access
 *    to the library.
 *
 * \param group A group to be used to add photo to the target album
 * \param assetURL The URL for the target asset
 * \param completion Block to be executed when succeed to add the asset to the assets library (camera roll)
 * \param failure Block to be executed when failed to add the asset to the custom photo album
 *
 * \return An ALAssetsLibraryAssetForURLResultBlock type block
 */
- (ALAssetsLibraryAssetForURLResultBlock)_assetForURLResultBlockWithGroup:(ALAssetsGroup *)group
                                                                 assetURL:(NSURL *)assetURL
                                                               completion:(ALAssetsLibraryWriteImageCompletionBlock)completion
                                                                  failure:(ALAssetsLibraryAccessFailureBlock)failure;

@end


@implementation ALAssetsLibrary (CustomPhotoAlbum)

#pragma mark - Private Method

- (ALAssetsLibraryWriteImageCompletionBlock)_resultBlockOfAddingToAlbum:(NSString *)albumName
                                                             completion:(ALAssetsLibraryWriteImageCompletionBlock)completion
                                                                failure:(ALAssetsLibraryAccessFailureBlock)failure
{
    return ^(NSURL *assetURL, NSError *error) {
        // Run the completion block for writing image to saved
        //   photos album
        //if (completion) completion(assetURL, error);
        
        // If an error occured, do not try to add the asset to
        //   the custom photo album
        if (error != nil) {
            if (failure) failure(error);
            return;
        }
        
        // Add the asset to the custom photo album
        [self addAssetURL:assetURL
                  toAlbum:albumName
               completion:completion
                  failure:failure];
    };
}

- (ALAssetsLibraryAssetForURLResultBlock)_assetForURLResultBlockWithGroup:(ALAssetsGroup *)group
                                                                 assetURL:(NSURL *)assetURL
                                                               completion:(ALAssetsLibraryWriteImageCompletionBlock)completion
                                                                  failure:(ALAssetsLibraryAccessFailureBlock)failure
{
    return ^(ALAsset *asset) {
        if ([group addAsset:asset]) {
            if (completion) completion(assetURL, nil);
        } else {
            NSString * message = [NSString stringWithFormat:@"ALAssetsGroup failed to add asset: %@.", asset];
            if (failure) failure([NSError errorWithDomain:@"LIB_ALAssetsLibrary_CustomPhotoAlbum"
                                                     code:0
                                                 userInfo:@{NSLocalizedDescriptionKey : message}]);
        }
    };
}

#pragma mark - Public Method

- (void)saveImage:(UIImage *)image
          toAlbum:(NSString *)albumName
       completion:(ALAssetsLibraryWriteImageCompletionBlock)completion
          failure:(ALAssetsLibraryAccessFailureBlock)failure
{
    [self writeImageToSavedPhotosAlbum:image.CGImage
                           orientation:(ALAssetOrientation)image.imageOrientation
                       completionBlock:[self _resultBlockOfAddingToAlbum:albumName
                                                              completion:completion
                                                                 failure:failure]];
}

- (void)saveVideo:(NSURL *)videoUrl
          toAlbum:(NSString *)albumName
       completion:(ALAssetsLibraryWriteImageCompletionBlock)completion
          failure:(ALAssetsLibraryAccessFailureBlock)failure
{
    [self writeVideoAtPathToSavedPhotosAlbum:videoUrl
                             completionBlock:[self _resultBlockOfAddingToAlbum:albumName
                                                                    completion:completion
                                                                       failure:failure]];
}

- (void)saveImageData:(NSData *)imageData
              toAlbum:(NSString *)albumName
             metadata:(NSDictionary *)metadata
           completion:(ALAssetsLibraryWriteImageCompletionBlock)completion
              failure:(ALAssetsLibraryAccessFailureBlock)failure
{
    [self writeImageDataToSavedPhotosAlbum:imageData
                                  metadata:metadata
                           completionBlock:[self _resultBlockOfAddingToAlbum:albumName
                                                                  completion:completion
                                                                     failure:failure]];
}

- (void)addAssetURL:(NSURL *)assetURL
            toAlbum:(NSString *)albumName
         completion:(ALAssetsLibraryWriteImageCompletionBlock)completion
            failure:(ALAssetsLibraryAccessFailureBlock)failure
{
    __block BOOL albumWasFound = NO;
    // 遍历相册文件夹
    [self enumerateGroupsWithTypes:ALAssetsGroupAlbum usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
        id groupName = [group valueForProperty:ALAssetsGroupPropertyName];
        if ([albumName compare:groupName] == NSOrderedSame) {
            // 如果存在已经创建的文件夹
            albumWasFound = YES;
            [self assetForURL:assetURL
                  resultBlock:[self _assetForURLResultBlockWithGroup:group
                                                            assetURL:assetURL
                                                          completion:completion
                                                             failure:failure]
                 failureBlock:failure];
            *stop = YES;
        }
        
        if (!group & !albumWasFound) {
            @nsm_weak(self)
            [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
                [PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle:albumName];
            } completionHandler:^(BOOL success, NSError * _Nullable error) {
                @nsm_strong(self)
                if (success) {
                    [self enumerateGroupsWithTypes:ALAssetsGroupAlbum usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
                        id groupName = [group valueForProperty:ALAssetsGroupPropertyName];
                        if ([albumName compare:groupName] == NSOrderedSame) {
                            // 如果存在已经创建的文件夹
                            [self assetForURL:assetURL
                                  resultBlock:[self _assetForURLResultBlockWithGroup:group
                                                                            assetURL:assetURL
                                                                          completion:completion
                                                                             failure:failure]
                                 failureBlock:failure];
                            *stop = YES;
                        }
                    } failureBlock:failure];
                } else
                    !failure?:failure(error);
            }];
        }
        
    } failureBlock:failure];
}

- (void)loadAssetsForProperty:(NSString *)property
                    fromAlbum:(NSString *)albumName
                   completion:(void (^)(NSMutableArray *, NSError *))completion
{
    ALAssetsLibraryGroupsEnumerationResultsBlock block = ^(ALAssetsGroup *group, BOOL *stop) {
        // Checking if library exists
        if (group == nil) {
            *stop = YES;
            return;
        }
        
        // If we have found library with given title we enumerate it
        if ([albumName compare:[group valueForProperty:ALAssetsGroupPropertyName]] == NSOrderedSame) {
            NSMutableArray * array = [[NSMutableArray alloc] init];
            [group enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
                // Checking if group isn't empty
                if (! result) return;
                
                [array addObject:(property ? ([result valueForProperty:property] ?: [NSNull null]) : result)];
            }];
            
            // Execute the |completion| block
            if (completion) completion(array, nil);
            
            // Album was found, bail out of the method
            *stop = YES;
        }
    };
    
    ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError *error) {
        NSLog(@"%s: %@", __PRETTY_FUNCTION__, [error localizedDescription]);
        if (completion) completion(nil, error);
    };
    
    [self enumerateGroupsWithTypes:ALAssetsGroupAll
                        usingBlock:block
                      failureBlock:failureBlock];
}

- (void)loadImagesFromAlbum:(NSString *)albumName
                 completion:(void (^)(NSMutableArray *, NSError *))completion
{
    ALAssetsLibraryGroupsEnumerationResultsBlock block = ^(ALAssetsGroup *group, BOOL *stop) {
        // Checking if library exists
        if (group == nil) {
            *stop = YES;
            return;
        }
        
        // If we have found library with given title we enumerate it
        if ([albumName compare:[group valueForProperty:ALAssetsGroupPropertyName]] == NSOrderedSame) {
            NSMutableArray * images = [[NSMutableArray alloc] init];
            [group enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
                // Checking if group isn't empty
                if (! result) return;
                
                // Getting the image from the asset
                UIImageOrientation orientation =
                (UIImageOrientation)[[result valueForProperty:@"ALAssetPropertyOrientation"] intValue];
                UIImage * image = [UIImage imageWithCGImage:[[result defaultRepresentation] fullScreenImage]
                                                      scale:1.0
                                                orientation:orientation];
                // Saving this image to the array
                [images addObject:image];
            }];
            
            // Execute the |completion| block
            if (completion) completion(images, nil);
            
            // Album was found, bail out of the method
            *stop = YES;
        }
    };
    
    ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError *error) {
        if (completion) completion(nil, error);
    };
    
    [self enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:block failureBlock:failureBlock];
}

@end
